<template>
  <el-dialog
    v-model="centerDialogVisible"
    :title="props.title"
    align-center
    :fullscreen="true"
    append-to-body
    :close-on-click-modal="false"
  >
    <div class="top">
      <div class="title">{{ title }}</div>
      <el-button-group class="buttons">
        <el-button size="small" plain @click="downloadImg()">保存图片</el-button>
        <el-button size="small" plain @click="setMinimap()">缩略图</el-button>
        <el-button size="small" plain @click="test()">测试</el-button>
        <el-button size="small" plain @click="submit()">保存</el-button>
      </el-button-group>
      <div style="clear: both"></div>
    </div>
    <div class="litegraph-page">
      <div class="litegraph-canvas" ref="root"></div>
    </div>
    <el-dialog v-model="createNodeVisible" width="500">
      <el-form>
        <el-form-item>
          <el-select
            v-model="createNode.key"
            filterable
            :filter-method="nodeSearch"
            placeholder="选择节点"
          >
            <el-option v-for="item in nodes" :key="item.id" :label="item.name" :value="item.id">
              <template v-if="item.type == 'SQL'">
                <el-tag type="warning" size="small">SQL</el-tag>
              </template>
              <template v-if="item.type == '条件'">
                <el-tag type="warning" size="small">条件</el-tag>
              </template>
              <template v-if="item.type == '服务'">
                <el-tag type="danger" size="small">服务</el-tag>
              </template>
              <template v-if="item.type == '变量'">
                <el-tag type="success" size="small">变量</el-tag>
              </template>
              <template v-if="item.type == '逻辑'">
                <el-tag type="danger" size="small">逻辑</el-tag>
              </template>
              <template v-if="item.type == '请求'">
                <el-tag type="danger" size="small">请求</el-tag>
              </template>
              <template v-if="item.type == '实体'">
                <el-tag type="primary" size="small">实体</el-tag>
              </template>
              <template v-if="item.type == '工具'">
                <el-tag type="primary" size="small">工具</el-tag>
              </template>
              <span style="margin-left: 5px; margin-right: 5px"></span>
              <i :class="item.icon"></i>
              <span style="margin-left: 5px">{{ item.name }}</span>
            </el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="createNodeVisible = false">{{ t('common.cancel') }}</el-button>
          <el-button type="primary" @click="createNodeSubmit()">
            {{ t('common.ok') }}
          </el-button>
        </div>
      </template>
    </el-dialog>
    <select-table ref="selectTable" />
    <select-table-column ref="selectTableColumn" />
    <select-sql ref="selectSql" />
    <update-group ref="updateGroup" />
    <update-param-name ref="updateParamName" />
    <update-date-time-format ref="updateDateTimeFormat" />
    <update-param-name-value ref="updateParamNameValue" />
    <update-data-index ref="updateDataIndex" />
    <update-expression ref="updateExpression" />
    <select-message-code ref="selectMessageCode" />
    <select-service ref="selectService" />
    <update-data-param-key ref="updateDataParamKey" />
    <design-test ref="designTest" />
    <update-order-by ref="updateOrderBy" />
  </el-dialog>
</template>
<script setup lang="ts">
import { reactive, ref, computed, watchEffect, toRaw, watch } from 'vue'
import { ElLoading, ElMessage, ElMessageBox } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n'
import { Canvas } from 'butterfly-dag'
import * as node from './components'
import 'butterfly-dag/dist/index.css'
import 'bootstrap-icons/font/bootstrap-icons.css'
import SelectTable from './components/SelectTable.vue'
import SelectTableColumn from './components/SelectTableColumn.vue'
import SelectSql from './components/SelectSql.vue'
import UpdateGroup from './components/UpdateGroup.vue'
import UpdateParamName from './components/UpdateParamName.vue'
import UpdateDateTimeFormat from './components/UpdateDateTimeFormat.vue'
import UpdateParamNameValue from './components/UpdateParamNameValue.vue'
import UpdateDataIndex from './components/UpdateDataIndex.vue'
import UpdateExpression from './components/UpdateExpression.vue'
import SelectMessageCode from './components/SelectMessageCode.vue'
import SelectService from './components/SelectService.vue'
import DesignTest from './components/DesignTest.vue'
import selectData from './components/selectData'
import UpdateDataParamKey from './components/UpdateDataParamKey.vue'
import UpdateOrderBy from './components/UpdateOrderBy.vue'
import request from '@/axios'
import { ServiceEditProps } from './types'

const centerDialogVisible = ref(false)

const createNodeVisible = ref(false)

const props = withDefaults(defineProps<ServiceEditProps>(), {
  designContext: 'designContext',
  appName: 'appName',
  excludeNode: () => {
    return []
  }
})

const createNode = reactive({
  top: 0,
  left: 0,
  key: ''
})

const formData = ref<any>({})

const designContext = ref<{ nodes: any[]; edges: any[]; groups: any[] }>()

const title = computed(() => {
  return formData.value.name + '(' + formData.value.code + ')'
})

const nodes = ref<any[]>([])

//搜索
const nodeSearch = (value) => {
  const list: any[] = []
  for (const key in selectData) {
    const item: any = selectData[key]
    if (
      (item.name.indexOf(value) > -1 ||
        item.query.indexOf(value) > -1 ||
        item.type.indexOf(value) > -1) &&
      !props.excludeNode.includes(key)
    ) {
      item['id'] = key
      list.push(item)
    }
  }
  nodes.value = list
}

const { t } = useI18n()
//根DIV
const root = ref()

const appName = ref('rbac-server')

//选择表
const selectTable = ref()
//选择表列
const selectTableColumn = ref()
//选择SQL
const selectSql = ref()

//修改组
const updateGroup = ref()

//修改参数名称
const updateParamName = ref()

//修改时间格式化
const updateDateTimeFormat = ref()

//修改参数名称和值
const updateParamNameValue = ref()

//修改数据索引
const updateDataIndex = ref()

//修改逻辑表达式
const updateExpression = ref()

//修改消息编码
const selectMessageCode = ref()

//修改调用服务
const selectService = ref()

//修改条件的属性和超参数名称
const updateDataParamKey = ref()

//修改排序方式
const updateOrderBy = ref()

//配置测试
const designTest = ref()
const test = () => {
  designTest.value.show(toRaw(formData.value), props.testUrl)
}

let canvas = ref()

//保存图片
const downloadImg = () => {
  canvas.value.save2img({ type: 'png', width: 1920, height: 1080, quality: 1 }).then((dataUrl) => {
    var link = document.createElement('a')
    link.download = props.title + '.png'
    link.href = dataUrl
    link.click()
  })
}

//创建节点
const createNodeSubmit = () => {
  canvas.value.addNode({
    top: createNode.top,
    left: createNode.left,
    Class: selectData[createNode.key].Class
  })
  createNodeVisible.value = false
}

//提交数据
const submit = async () => {
  const mapData = canvas.value.getDataMap()
  const nodes: any[] = []
  console.log(mapData)

  //处理node数据
  mapData.nodes.forEach((n) => {
    const _node = {}
    _node['group'] = n.group
    for (const key in n.options) {
      //去除零时属性
      if (key.indexOf('_') == 0) {
        continue
      }
      if (key == 'Class') {
        _node[key] = n.options[key].name
      } else if (key == 'endpoints') {
        const _endpoints: any[] = []
        n.options[key].forEach((e) => {
          const _endpoint = {}
          for (const eKey in e) {
            if (eKey == 'Class') {
              _endpoint[eKey] = e[eKey].name
            } else {
              _endpoint[eKey] = e[eKey]
            }
          }
          _endpoints.push(_endpoint)
        })
        _node[key] = _endpoints
      } else {
        _node[key] = n.options[key]
      }
    }
    _node['top'] = n.top
    _node['left'] = n.left
    nodes.push(_node)
  })

  //处理连线
  const edges: any[] = []
  mapData.edges.forEach((e) => {
    const _edge: any = {}
    for (const key in e.options) {
      //去除零时属性
      if (key.indexOf('_') == 0) {
        continue
      }
      _edge[key] = e.options[key]
    }
    _edge.source = _edge.sourceEndpoint
    _edge.target = _edge.targetEndpoint
    edges.push(_edge)
  })
  const groups: any[] = []
  mapData.groups.forEach((e) => {
    const _group = {
      draggable: e.draggable,
      expand: e.expand,
      height: e.height,
      id: e.id,
      left: e.left,
      resize: e.resize,
      scope: e.scope,
      top: e.top,
      width: e.width,
      options: e.options
    }
    groups.push(_group)
  })

  const _designContext = {
    nodes: nodes,
    edges: edges,
    groups: groups
  }
  formData.value[props.designContext] = JSON.stringify(_designContext)
  const loading = ElLoading.service({
    lock: true,
    text: t('common.submitMessage'),
    background: 'rgba(0, 0, 0, 0.7)'
  })
  const rs = await request.put({ url: props.submitUrl, data: toRaw(formData.value) }, loading)
  if (rs.success) {
    ElMessage.success(rs.message)
  }
}

const load = async (params) => {
  const loading = ElLoading.service({
    lock: true,
    text: t('common.loadMessage'),
    background: 'rgba(0, 0, 0, 0.7)'
  })
  const rs = await request.get({ url: props.loadUrl, params }, loading)
  if (rs.success) {
    formData.value = rs.data
    appName.value = rs.data[props.appName]
    if (rs.data[props.designContext]) {
      const _designContext = JSON.parse(rs.data[props.designContext])
      _designContext.nodes.forEach((n) => {
        n.Class = selectData[n.Class].Class
        n.endpoints.forEach((ed) => {
          ed.Class = node.BaseEndpoint
        })
      })
      _designContext.groups.forEach((g) => {
        g.Class = node.BaseGroup
      })
      _designContext.edges.forEach((_edge) => {
        _edge.source = _edge.sourceEndpoint
        _edge.target = _edge.targetEndpoint
      })
      designContext.value = _designContext
    } else {
      designContext.value = {
        nodes: [],
        edges: [],
        groups: []
      }
    }
  } else {
    ElMessage.error(t('common.loadError'))
  }
}

watchEffect(() => {
  if (root.value && !canvas.value) {
    //创建画布
    createDag()
  }
})

watch(designContext, (_value) => {
  //绘制数据图
  canvasDrag()
})
const minimapSate = ref(true)
const setMinimap = () => {
  minimapSate.value = !minimapSate.value
  canvas.value.setMinimap(minimapSate.value)
}

const canvasDrag = () => {
  canvas.value.redraw(designContext.value, () => {
    canvas.value.setGridMode(true, {
      isAdsorb: false, // 是否自动吸附,默认关闭
      theme: {
        shapeType: 'circle', // 展示的类型，支持line & circle
        gap: 30, // 网格间隙
        background: 'rgba(0, 0, 0, 0.65)', // 网格背景颜色
        circleRadiu: 0.5, // 圆点半径
        circleColor: 'rgba(255, 255, 255, 0.8)' // 圆点颜色
      }
    })
  })
}

const createDag = () => {
  canvas.value = new Canvas({
    root: root.value, //canvas的根节点(必传)
    zoomable: true, //可缩放(选填)
    moveable: true, //可平移(选填)
    draggable: true, //节点可拖动(选填)
    linkable: true, //节点可连接(选填)
    disLinkable: true, //节点可取消连接(选填)
    isSelectMode: true, //框选模式
    theme: {
      //鼠标放大缩小间隙设置
      autoFixCanvas: {
        //节点拖动或连线拖动到画布边缘时，画布自动延展
        enable: true,
        autoMovePadding: [100, 100, 100, 100] //触发自动延展的画布内边距
      },
      edge: {
        type: 'endpoint', //线段连接类型
        shapeType: 'AdvancedBezier', //线条默认类型
        hasRadius: false, //默认曼哈顿曲线不为圆角
        //label: 'test', //线条默认label
        arrow: false, //线条默认是否带箭头
        arrowPosition: 0.5, //箭头位置(0 ~ 1)
        arrowOffset: 0.0, //箭头偏移
        //arrowShapeType: ' bi-cursor-fill', //自定义箭头样式
        Class: node.BaseEdge, //自己拓展的class,拖动连线的时候会采用该拓展类
        isExpandWidth: false, //增加线条交互区域
        defaultAnimate: false, //默认开启线条动画
        dragEdgeZindex: 499 //线段的默认z-index(选填，默认：499)
      },
      endpoint: {
        position: [], //限制锚点位置['Top', 'Bottom', 'Left', 'Right'],
        linkableHighlight: true, //连线时会触发point.linkable的方法，可做高亮
        limitNum: 1, //限制锚点的连接数目
        expandArea: {
          //锚点过小时，可扩大连线热区
          left: 15,
          right: 15,
          top: 15,
          botton: 15
        }
      },
      autoResizeRootSize: true, // 自动适配root大小，默认为true
      isMouseMoveStopPropagation: true // 拖动事件是否阻止冒泡
    }
  })
  root.value.oncontextmenu = (e) => {
    const offset = canvas.value.getOffset()
    createNode.top = e.layerY - offset[1]
    createNode.left = e.layerX - offset[0]
    createNodeVisible.value = true
    return false
  }
  //节点删除
  canvas.value.on('onNodeContextmenuEvent', (node) => {
    ElMessageBox.confirm(t('common.delMessage'))
      .then(() => {
        canvas.value.removeNode(node.id)
      })
      .catch(() => {
        // catch error
      })
  })
  //触发更新sql事件
  canvas.value.on('onModifyExeSqlEvent', (data) => {
    selectSql.value.show(appName, (sqlId) => {
      //触发更新
      canvas.value.emit('onUpdateExeSqlEvent', {
        id: data.id,
        exeSql: sqlId
      })
    })
  })

  //触发更新数据表事件
  canvas.value.on('onModifyDbTableEvent', (data) => {
    selectTable.value.show(appName, (dbName) => {
      //触发更新
      canvas.value.emit('onUpdateDbTableEvent', {
        id: data.id,
        dbTable: dbName
      })
    })
  })

  //触发更新数据表和列事件
  canvas.value.on('onModifyDbTableColumnEvent', (data) => {
    selectTableColumn.value.show(appName, (dbName, column) => {
      //触发更新
      canvas.value.emit('onUpdateDbTableColumnEvent', {
        id: data.id,
        dbTable: dbName,
        column: column
      })
    })
  })

  //触发Group的修改事件
  canvas.value.on('onModifyGroupEvent', (data) => {
    updateGroup.value.show(data.text, (text) => {
      //触发更新
      canvas.value.emit('onUpdateGroupEvent', {
        id: data.id,
        text: text
      })
    })
  })

  //触发ParamName的修改事件
  canvas.value.on('onModifyParamNameEvent', (data) => {
    updateParamName.value.show(data.paramName, (paramName) => {
      //触发更新
      canvas.value.emit('onUpdateParamNameEvent', {
        id: data.id,
        paramName: paramName
      })
    })
  })

  //触发修改时间格式化
  canvas.value.on('onModifyDateTimeFormatEvent', (data) => {
    updateDateTimeFormat.value.show(data.format, (format) => {
      //触发更新
      canvas.value.emit('onUpdateDateTimeFormatEvent', {
        id: data.id,
        format: format
      })
    })
  })

  //触发ParamName和paramValue的修改事件
  canvas.value.on('onModifyParamNameAndValueEvent', (data) => {
    updateParamNameValue.value.show(data.paramName, data.paramValue, (paramName, paramValue) => {
      //触发更新
      canvas.value.emit('onUpdateParamNameAndValueEvent', {
        id: data.id,
        paramName: paramName,
        paramValue: paramValue
      })
    })
  })

  //触发数据索引的修改事件
  canvas.value.on('onModifyDataIndexEvent', (data) => {
    updateDataIndex.value.show(data.dataIndex, (dataIndex) => {
      //触发更新
      canvas.value.emit('onUpdateDataIndexEvent', {
        id: data.id,
        dataIndex: dataIndex
      })
    })
  })
  //触发逻辑表达式的修改事件
  canvas.value.on('onModifyExpressionEvent', (data) => {
    updateExpression.value.show(data.expression, (expression) => {
      //触发更新
      canvas.value.emit('onUpdateExpressionEvent', {
        id: data.id,
        expression: expression
      })
    })
  })

  //触发消息编码事件
  canvas.value.on('onModifyMessageCodeEvent', (data) => {
    selectMessageCode.value.show(appName, (messageCode) => {
      //触发更新
      canvas.value.emit('onUpdateMessageCodeEvent', {
        id: data.id,
        messageCode: messageCode
      })
    })
  })
  //触发微服务变更事件
  canvas.value.on('onModifyServiceCodeEvent', (data) => {
    selectService.value.show((appName, code) => {
      //触发更新
      canvas.value.emit('onUpdateServiceCodeEvent', {
        id: data.id,
        serviceCode: code,
        appName: appName
      })
    })
  })

  //触发查询条件参数和属性名称事件
  canvas.value.on('onModifyWhereDataParamKeyEvent', (data) => {
    updateDataParamKey.value.show(data.dataKey, data.paramKey, (dataKey, paramKey) => {
      //触发更新
      canvas.value.emit('onUpdateWhereDataParamKeyEvent', {
        id: data.id,
        dataKey: dataKey,
        paramKey: paramKey
      })
    })
  })

  //触发修改排序方式变更
  canvas.value.on('onModifyOrderTypeParamKeyEvent', (data) => {
    updateOrderBy.value.show(data.paramKey, data.orderType, (paramKey, orderType) => {
      //触发更新
      canvas.value.emit('onUpdateOrderTypeParamKeyEvent', {
        id: data.id,
        paramKey: paramKey,
        orderType: orderType
      })
    })
  })
  canvas.value.setMinimap(true)
}

const show = async (params) => {
  load(params)
  centerDialogVisible.value = true
}

//关闭表单
const hide = () => {
  centerDialogVisible.value = false
}
//暴露方法
defineExpose({ show, hide })
</script>

<style lang="less">
.top {
  padding: 5px;
  .title {
    float: left;
  }
  .buttons {
    float: right;
  }
}

.litegraph-page {
  position: relative;
  width: 100%;
  height: calc(100vh - 160px);
  background: #090a0a;
  .litegraph-canvas {
    width: 100%;
    height: 100%;
    .group {
      width: 550px;
      height: 240px;
      position: absolute;
      .container {
        width: 100%;
        height: 100%;
        max-width: 100%;
        border-radius: 0px 0px 5px 5px;
        border: 2px solid #0088ff;
        background: #4c5f82;
        opacity: 0.3;
      }
    }
    .group {
      > .text {
        position: absolute;
        left: 18px;
        font-size: 20px;
        background-color: #003daa63;
        top: -36px;
        left: 0px;
        right: -4px;
        padding: 5px;
        border-radius: 5px 5px 0px 0px;
        color: #16c7da;
        .title {
          width: calc(100% - 40px);
          overflow: hidden; //隐藏文字
          text-overflow: ellipsis; //显示 ...
          white-space: nowrap; //不换行
          background-color: transparent;
          color: #2a80ff;
          font-size: 16px;
          float: left;
        }
        .icon {
          cursor: pointer;
        }
        .icon:hover {
          color: #0088ff;
        }
      }
      > .text:hover {
        cursor: move;
      }
      .titlt-expend {
        border-radius: 5px;
      }
    }
    //link连线样式
    .base-link {
      stroke: #016a08;
      stroke-width: 3px;
      &.active {
        stroke-width: 3px;
        stroke: #03b435;
      }
    }
    .base-node {
      height: 32px;
      width: 102px;
      line-height: 28px;
      vertical-align: middle;
      position: absolute;
      background: #3c3a3a;
      text-align: center;
      &.active {
        border: 1px solid #d46001;
      }
      .text {
        display: inline-block;
        width: 100%;
        height: 30px;
        cursor: move;
        vertical-align: middle;
      }
    }

    //point连线样式
    .point-left-title {
      font-size: 12px;
      padding-right: 5px;
      white-space: nowrap; //不换行
      color: #00ff62;
      position: relative;
      top: -7px;
    }
    .point-right-title {
      font-size: 12px;
      padding-left: 5px;
      white-space: nowrap; //不换行
      color: #ff0000;
      position: relative;
      top: -8px;
      left: 10px;
    }

    .input-point {
      background: #ff0000;
      width: 10px;
      height: 10px;
      position: absolute;
      border-radius: 50%;
      cursor: pointer;
    }

    .point-linked {
      border-color: #034ef1;
      background-color: #034ef1;
      .point-right-title {
        color: #034ef1;
      }
      .point-left-title {
        color: #034ef1;
      }
    }

    .input-point-linkable {
      background: #dc8903;
      .point-right-title {
        color: #dc8903;
      }
    }

    .input-point-hover {
      background: #24b895;
      .point-right-title {
        color: #24b895;
      }
    }

    .output-point {
      position: absolute;
      width: 10px;
      height: 10px;
      border-radius: 50%;
      cursor: pointer;
      background: #00ff62;
    }

    .output-point-linkable {
      background: #dc8903;
      .point-right-title {
        color: #dc8903;
      }
    }

    .output-point-hover {
      background: #008a12;
      .point-right-title {
        color: #008a12;
      }
    }

    .run-start-point {
      position: absolute;
      width: 10px;
      height: 10px;
      border-radius: 0px;
      background: #ff0000;
    }

    .run-start-point-linkable {
      background: #dc8903;
    }

    .run-start-point-hover {
      background: #008a12;
    }

    .run-end-point {
      position: absolute;
      background: #008a12;
      border-radius: 0px;
      width: 10px;
      height: 10px;
    }

    .run-end-point-linkable {
      background: #dc8903;
    }

    .run-end-point-hover {
      background: #008a12;
    }
  }
}
</style>
